/**
 * @Description: 人物动作行为
 * @author Beon
 * @date 2022/4/19
*/
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import * as THREE from 'three';
import direction from './Direction';
import { AnimationMixer } from 'three/src/animation/AnimationMixer';
import { subscribe } from '../../../../utils/tools/tools';
// import { PropertyBinding } from 'three';
import { AnimationAction } from 'three/src/animation/AnimationAction';
import { KeyframeTrack } from 'three';
import { AnimationClip } from 'three';

interface ActionTypes {
  stand: AnimationAction
  running: AnimationAction
}

export default class Role {
  mixer = null as AnimationMixer | null
  gltf = null as GLTF | null

  private clock = new THREE.Clock();
  private baseConfig = {
    duration: 0.38 // 动作延迟时间
  }
  private actions: ActionTypes | undefined

  constructor(scene: any) {
    const loader = new GLTFLoader();
    loader.load('glb/123.glb', (gltf) => {
      scene.add(gltf.scene);
      this.mixer = new THREE.AnimationMixer(gltf.scene);
      this.gltf = gltf;
      this.actions = this.initRoleAnimation();
      // this.actions.stand.loop = THREE.LoopOnce;
      this.actions.stand.play();
      this.createEventListen();
      subscribe.on('directionAn', 'camera', this.roleAnimate.bind(this));
    }, (...arg) => {

      // console.log(...arg);

    }, function(error) {

      // console.log(error);

    });
  }

  private initRoleAnimation():ActionTypes {
    const mixer = this.mixer as AnimationMixer;
    const gltf = this.gltf as GLTF;

    function splitByTime(name: string, start: number, end: number) {
      let getMaxTime = -1;
      const tracks = gltf.animations[0].tracks.map((item) => {
        const setTime = item.values.length / item.times.length;
        let findFirst = -1;
        const times = item.times.filter((time, index) => {
          const check = time >= start && time <= end;
          findFirst === -1 && check && (findFirst = index);
          return check;
        }).map((item) => {
          const realTime = item - start;
          realTime > getMaxTime && (getMaxTime = realTime);
          return realTime;
        });
        const values = item.values.slice(findFirst * setTime, findFirst * setTime + times.length * setTime);
        return new KeyframeTrack(item.name, times, values, THREE.InterpolateDiscrete);
      });
      return new AnimationClip(name, getMaxTime - 0.1, tracks);
    }

    return {
      stand: mixer.clipAction(splitByTime('walk', 7.4, 10.1)),
      running: mixer.clipAction(splitByTime('running', 6.35, 7.35))
    };
  }

  private roleAnimate = (() => {
    // 记录上一次点
    const oldDirection = new THREE.Vector3();
    // 记录上上次点
    const oldVector3 = new THREE.Vector3(0, 0, -1);
    return (param: {
      vector3Direction: THREE.Vector3,
      changeVector3: THREE.Vector3
    }) => {
      const dt = this.clock.getDelta();
      if (this.mixer && this.mixer.update) this.mixer.update(dt);

      const {
        vector3Direction
      } = param;

      this.gltf?.scene.position.copy(vector3Direction);

      if (vector3Direction.equals(oldDirection)) return;
      const dealVector3 = new THREE.Vector3().subVectors(oldDirection, vector3Direction);
      const dealOldVertor3 = new THREE.Vector3().subVectors(oldVector3, oldDirection);
      this.gltf?.scene?.rotation && (this.gltf.scene.rotation.y += dealOldVertor3.angleTo(dealVector3) * (dealOldVertor3.cross(dealVector3).y > 0 ? 1 : -1));

      oldVector3.copy(oldDirection);
      oldDirection.copy(vector3Direction);
    };
  })()

  actionDeal = (() => {
    const runningCheck = {
      KeyW: false,
      KeyS: false,
      KeyA: false,
      KeyD: false
    };

    const judgeRun = (() => {
      let runningStatus = false;
      return (type: 'KeyW' | 'KeyS' | 'KeyA' | 'KeyD', status: boolean) => {
        runningCheck[type] = status;

        if (runningStatus && !status && Object.values(runningCheck).every((item) => !item)) {
          runningStatus = false;

          this.actions?.running.fadeOut(this.baseConfig.duration);
          this.actions?.stand.reset().play().fadeIn(this.baseConfig.duration);
        }

        if (!runningStatus && status) {
          runningStatus = true;

          this.actions?.stand.fadeOut(this.baseConfig.duration);
          this.actions?.running.reset().play().fadeIn(this.baseConfig.duration);
        }
      };
    })();

    return {
      keyDown: (type: string) => {
        switch (type) {
          case 'KeyW':
          case 'KeyS':
          case 'KeyA':
          case 'KeyD':
            judgeRun(type, true);
        }
      },
      keyUp: (type: string) => {
        switch (type) {
          case 'KeyW':
          case 'KeyS':
          case 'KeyA':
          case 'KeyD':
            judgeRun(type, false);
        }
      }
    };
  })()

  createEventListen() {
    document.body.addEventListener('keydown', (e) => {
      direction.open(e.code);
      this.actionDeal.keyDown(e.code);
    });

    document.body.addEventListener('keyup', (e) => {
      direction.close(e.code);
      this.actionDeal.keyUp(e.code);
    });
  }
}
